package org.changs.servlet.net;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;

/**
 * Created by yincs on 2017/9/16.
 */

public class NetworkDeviceScanner1 {

    private final List<NetworkAddress> mNetworkAddress = new ArrayList<>(5);
    private final AtomicInteger mIndex = new AtomicInteger(0);
    private NetworkDeviceScanner.ScannerHandler mScannerHandler;
    private int mNumberOfThreads = 6;
    private Disposable scanSubscribe;

    public NetworkDeviceScanner1() {
    }

    public NetworkDeviceScanner1(int numberOfThreads) {
        mNumberOfThreads = numberOfThreads;
    }

    public void stop() {
        if (scanSubscribe != null && !scanSubscribe.isDisposed()) {
            scanSubscribe.dispose();
        }
        scanSubscribe = null;
        mScannerHandler = null;
        mNetworkAddress.clear();
        mIndex.set(0);
    }

    public boolean scan(ArrayList<String> interfaces, NetworkDeviceScanner.ScannerHandler handler) {
        if (interfaces.size() == 0) {
            Timber.d("scan error: interfaces.size() == 0");
            return false;
        }
        stop();
        mScannerHandler = handler;
        for (String ipAddress : interfaces) {
            mNetworkAddress.add(new NetworkAddress(NetworkUtils.getAddressPrefix(ipAddress)));
        }
        List<Observable<Boolean>> observables = new ArrayList<>();
        for (int i = 0; i < mNumberOfThreads; i++) {
            Observable<Boolean> observable = Observable
                    .fromCallable(new Callable<Boolean>() {
                        @Override
                        public Boolean call() throws Exception {
                            new Scanner().run();
                            return true;
                        }
                    })
                    .subscribeOn(Schedulers.io());
            observables.add(observable);
        }
        scanSubscribe = Observable
                .zip(observables, new Function<Object[], Object>() {
                    @Override
                    public Object apply(@NonNull Object[] objects) throws Exception {
                        return true;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(@NonNull Object o) throws Exception {
                        if (mScannerHandler != null)
                            mScannerHandler.onScannerCompleted();
                    }
                });
        return true;
    }

    public void setLock(boolean lock) {
    }

    public void interrupt() {
        if (mScannerHandler != null) {
            mScannerHandler.onScannerCompleted();
        }
        stop();
    }


    private class Scanner implements Runnable {

        @Override
        public void run() {
            while (true) {
                int index = mIndex.get();
                Timber.d("index = " + index);
                if (mNetworkAddress.size() <= index) return;
                NetworkAddress networkAddress = mNetworkAddress.get(index);
                int position = networkAddress.getSite().getAndIncrement();
                if (position <= 255) {
                    try {
                        InetAddress inetAddress = InetAddress.getByName(networkAddress.getPrefix() + position);
                        Timber.d("inetAddress = " + inetAddress + " Thread Name" + Thread.currentThread().getName());
                        if (inetAddress.isReachable(300)) {
                            Timber.d("run: 可连接");
                            if (mScannerHandler != null) mScannerHandler.onDeviceFound(inetAddress);
                        }
                    } catch (Exception e) {
                        Timber.d("run: 不可连接");
//                        e.printStackTrace();
                    }
                } else {
                    mIndex.incrementAndGet();
                }
            }
        }
    }
}
